// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DBInstrumentStaticDataHelpers.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge.DataModel
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Data.Objects.DataClasses;
    using System.Linq;

    using Org.OpenTrader.Framework.Forge.DataCache;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.LinqKit;

    #endregion

    /// <summary>
    /// The db instrument helpers.
    /// </summary>
    public partial class DBInstrumentStaticDataHelpers
    {
        #region Public Methods

        /// <summary>
        /// The create instrument.
        /// </summary>
        /// <param name="marketId">
        /// The marketId.
        /// </param>
        /// <param name="tickSizeRuleId">
        /// The tickSizeRuleId.
        /// </param>
        /// <param name="instrumentType">
        /// The instrumentType.
        /// </param>
        /// <param name="instrumentName">
        /// The instrumentName.
        /// </param>
        /// <param name="instrumentCode">
        /// The instrumentCode.
        /// </param>
        /// <param name="instrumentIsin">
        /// The instrumentIsin.
        /// </param>
        /// <param name="instrumentCurrency">
        /// The instrument Currency.
        /// </param>
        /// <param name="tradingScope">
        /// The tradingScope.
        /// </param>
        /// <returns>
        /// </returns>
        public static IInstrumentStaticData CreateInstrumentStaticData(
            Guid marketId, 
            Guid tickSizeRuleId, 
            string instrumentType, 
            string instrumentName, 
            string instrumentCode, 
            string instrumentIsin, 
            ECurrency instrumentCurrency, 
            ETradingScope tradingScope)
        {
            instrumentType = instrumentType.Replace(typeof(InstrumentStaticData).Namespace + ".InstrumentStaticData", string.Empty);

            var dbMarket = DBMarketHelpers.GetDBMarket(marketId);
            if (dbMarket != null)
            {
                var dbInstrument = DBInstrumentStaticData.CreateDBInstrumentStaticData(
                    Guid.NewGuid(), 
                    instrumentType, 
                    instrumentName, 
                    instrumentCode, 
                    instrumentIsin, 
                    tradingScope.ToString(), 
                    instrumentCurrency.ToString(), 
                    tickSizeRuleId, 
                    true, 
                    true, 
                    true, 
                    true, 
                    false, 
                    false, 
                    false, 
                    0.0, 
                    0.0, 
                    0.2);
                dbMarket.DBInstrumentStaticData.Add(dbInstrument);

                if (Singleton<DBForge>.Instance.Commit() > 0)
                {
                    return GetInstrumentStaticData<IInstrumentStaticData>(dbInstrument.ID);
                }
            }

            return null;
        }

        /// <summary>
        /// The get db instrument.
        /// </summary>
        /// <param name="instrumentID">
        /// The instrumentID.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// </returns>
        public static T GetDBInstrumentStaticData<T>(Guid instrumentID) where T : EntityObject, IDBInstrumentStaticData
        {
            // Create a new predicate, initially to 'true' (returns everything)
            var query = PredicateBuilder.True<T>();

            // 'true' And 'o.ID' == 'instrumentID' (returns only instrument with given instrumentID)
            query = query.And(o => o.ID == instrumentID);

            // Fetch the T (DBInstrumentStaticDataSomething : IDBInstrumentStaticData) with given ID
            var dbInstruments = Singleton<DBForge>.Instance.Entities.CreateQuery<T>(string.Format("[{0}]", typeof(T).Name)).Where(query.Compile());

            // ID is a primary key so you can either have 0 or 1 result
            if (dbInstruments.Count() == 1)
            {
                return dbInstruments.First() as T;
            }

            return null;
        }

        /// <summary>
        /// The get db instrument.
        /// </summary>
        /// <param name="instrumentID">
        /// The instrumentID.
        /// </param>
        /// <returns>
        /// </returns>
        public static DBInstrumentStaticData GetDBInstrumentStaticData(Guid instrumentID)
        {
            // Fetch the DBInstrumentStaticData with given ID
            var dbInstruments = from i in Singleton<DBForge>.Instance.Entities.DBInstrumentStaticData where i.ID == instrumentID select i;

            // ID is a primary key so you can either have 0 or 1 result
            if (dbInstruments.Count() == 1)
            {
                return dbInstruments.First();
            }

            return null;
        }

        /// <summary>
        /// The get db instrument static data.
        /// </summary>
        /// <param name="dbMarket">
        /// The db market.
        /// </param>
        /// <param name="code">
        /// The code.
        /// </param>
        /// <param name="tradingScope">
        /// The trading scope.
        /// </param>
        /// <returns>
        /// </returns>
        public static DBInstrumentStaticData GetDBInstrumentStaticData(DBMarket dbMarket, string code, ETradingScope tradingScope)
        {
            var tradingScopeValue = tradingScope.ToString();

            // Fetch the DBInstrumentStaticData with given ID
            var dbInstruments = from i in dbMarket.DBInstrumentStaticData where i.Code == code && i.TradingScope == tradingScopeValue select i;

            // ID is a primary key so you can either have 0 or 1 result
            if (dbInstruments.Count() == 1)
            {
                return dbInstruments.First();
            }

            return null;
        }

        /// <summary>
        /// Get the some T : IInstrumentStaticData with a given name, inside a given market 
        /// </summary>
        /// <typeparam name="T">
        /// </typeparam>
        /// <param name="instrumentID">
        /// The instrumentID.
        /// </param>
        /// <returns>
        /// </returns>
        public static T GetInstrumentStaticData<T>(Guid instrumentID) where T : IInstrumentStaticData
        {
            var dbInstruments = from i in Singleton<DBForge>.Instance.Entities.DBInstrumentStaticData where i.ID == instrumentID select i;

            IInstrumentStaticData instrumentStaticData = null;
            IDBInstrumentStaticData dbSpecializedInstrument = null;

            if (dbInstruments.Count() == 0)
            {
                goto fail;
            }

            var dbInstrument = dbInstruments.First();
            var dictionaryId = dbInstrument.DBMarket.DBDictionary.ID;
            if (dbInstrument.Type.Equals("Equity"))
            {
                dbSpecializedInstrument = GetDBInstrumentStaticData<DBInstrumentStaticDataEquity>(dbInstrument.ID);
                if (dbSpecializedInstrument == null)
                {
                    goto fail;
                }

                instrumentStaticData = new InstrumentStaticDataEquity(dictionaryId, dbSpecializedInstrument as DBInstrumentStaticDataEquity, dbInstrument);
            }
            else if (dbInstrument.Type.Equals("Forex"))
            {
                dbSpecializedInstrument = GetDBInstrumentStaticData<DBInstrumentStaticDataForex>(dbInstrument.ID);
                if (dbSpecializedInstrument == null)
                {
                    goto fail;
                }

                instrumentStaticData = new InstrumentStaticDataForex(dictionaryId, dbSpecializedInstrument as DBInstrumentStaticDataForex, dbInstrument);
            }
            else if (dbInstrument.Type.Equals("ForexFuture"))
            {
                dbSpecializedInstrument = GetDBInstrumentStaticData<DBInstrumentStaticDataForexFuture>(dbInstrument.ID);
                if (dbSpecializedInstrument == null)
                {
                    goto fail;
                }

                instrumentStaticData = new InstrumentStaticDataForexFuture(
                    dictionaryId, dbSpecializedInstrument as DBInstrumentStaticDataForexFuture, dbInstrument);
            }
            else if (dbInstrument.Type.Equals("EquityIndexFuture"))
            {
                dbSpecializedInstrument = GetDBInstrumentStaticData<DBInstrumentStaticDataEquityIndexFuture>(dbInstrument.ID);
                if (dbSpecializedInstrument == null)
                {
                    goto fail;
                }

                instrumentStaticData = new InstrumentStaticDataEquityIndexFuture(
                    dictionaryId, dbSpecializedInstrument as DBInstrumentStaticDataEquityIndexFuture, dbInstrument);
            }

            ////else if (dbInstrument.Type.Equals("EquityVanillaOption"))
            ////{
            ////    dbSpecializedInstrument =
            ////        GetDBInstrumentStaticData<DBInstrumentStaticDataEquityVanillaOption>(dbInstrument.ID);
            ////    if (dbSpecializedInstrument == null)
            ////    {
            ////        goto fail;
            ////    }
            ////    instrumentStaticData = new InstrumentStaticDataEquityVanillaOption(
            ////        dictionaryId, dbSpecializedInstrument as DBInstrumentStaticDataEquityVanillaOption, dbInstrument);
            ////}
            return (T)instrumentStaticData;

            fail:
            return default(T);
        }

        /// <summary>
        /// The load instruments.
        /// </summary>
        /// <param name="dictionaryId">
        /// The dictionaryId.
        /// </param>
        /// <returns>
        /// </returns>
        public static IInstrumentStaticData[] LoadInstrumentsStaticData(Guid dictionaryId)
        {
            var dbDictionaries = from d in Singleton<DBForge>.Instance.Entities.DBDictionary where d.ID == dictionaryId select d;

            if (dbDictionaries.Count() != 1)
            {
                return new IInstrumentStaticData[] { };
            }

            var dbDictionary = dbDictionaries.First();

            IList<IInstrumentStaticData> list = new List<IInstrumentStaticData>();

            dbDictionary.DBMarket.Load();
            foreach (var dbMarket in dbDictionary.DBMarket)
            {
                dbMarket.DBInstrumentStaticData.Load();

                var dbInstruments = from i in dbMarket.DBInstrumentStaticData orderby i.DBMarket.Name orderby i.Name select i;

                foreach (var dbInstrument in dbInstruments)
                {
                    var i = GetInstrumentStaticData<IInstrumentStaticData>(dbInstrument.ID);
                    if (i != null)
                    {
                        list.Add(i);
                    }
                }
            }

            return list.ToArray();
        }

        /// <summary>
        /// The save instrument.
        /// </summary>
        /// <param name="i">
        /// The i.
        /// </param>
        /// <returns>
        /// The save instrument.
        /// </returns>
        public static bool SaveInstrumentStaticDatas(IInstrumentStaticData i)
        {
            // Fetch the related instrument in database
            var dbInstrument = GetDBInstrumentStaticData(i.ID);

            // Is new
            if (dbInstrument == null)
            {
                dbInstrument = DBInstrumentStaticData.CreateDBInstrumentStaticData(
                    i.ID, 
                    i.GetType().Name, 
                    i.Name, 
                    i.Code, 
                    i.ISIN, 
                    i.TradingScope.ToString(), 
                    i.Currency.ToString(), 
                    i.TickSizeRule.ID, 
                    i.Enabled, 
                    i.BuyEnabled, 
                    i.SellEnabled, 
                    i.ShortSellEnabled, 
                    i.BlackListed, 
                    i.HardToBorrow, 
                    i.Restricted, 
                    i.MaxNominalTrade, 
                    i.MaxNominalBook, 
                    i.MaxVariationPercentage);
                Singleton<DBForge>.Instance.Entities.AddToDBInstrumentStaticData(dbInstrument);
            }

            // Reflection is so powerful !
            Reflection.CopyProperties(i, dbInstrument);

            var n = Singleton<DBForge>.Instance.Commit();

            // Save also wirings
            SaveInstrumentStaticDataWirings(i);

            // All done
            return true;
        }

        /// <summary>
        /// The save instrument wirings.
        /// </summary>
        /// <param name="i">
        /// The i.
        /// </param>
        public static void SaveInstrumentStaticDataWirings(IInstrumentStaticData i)
        {
            var dbInstrument = GetDBInstrumentStaticData(i.ID);
            if (dbInstrument != null)
            {
                if (i.FeedWiring != null)
                {
                    dbInstrument.DBFeedPluginID = i.FeedWiring.PluginID;
                    if (i.FeedWiring.SubscriptionConfiguration != null)
                    {
                        dbInstrument.DBFeedPluginInstrumentStaticDataConfigurationID = i.FeedWiring.SubscriptionConfiguration.ID;
                        Singleton<DC>.Instance.SaveConfiguration(i.FeedWiring.SubscriptionConfiguration);
                    }
                }

                if (i.OrderWiring != null)
                {
                    dbInstrument.DBOrderPluginID = i.OrderWiring.PluginID;
                    if (i.OrderWiring.OrderRoutingConfiguration != null)
                    {
                        dbInstrument.DBOrderPluginInstrumentStaticDataConfigurationID = i.OrderWiring.OrderRoutingConfiguration.ID;
                        Singleton<DC>.Instance.SaveConfiguration(i.OrderWiring.OrderRoutingConfiguration);
                    }
                }

                var n = Singleton<DBForge>.Instance.Commit();
            }
        }

        #endregion
    }
}